package com.tbit.main.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.tbit.main.constant.AccountConfigExtConstant;
import com.tbit.main.constant.DaoParamConstant;
import com.tbit.main.dao.core.ParkPointDao;
import com.tbit.main.h2.pojo.ParkPoint;
import com.tbit.main.pojo.*;
import com.tbit.main.service.*;
import com.tbit.main.util.GeoUtil;
import com.tbit.main.util.MapUtils;
import com.tbit.main.util.StringUtil;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

//@Service("parkPointService")
@Component
public class ParkPointServiceImpl implements ParkPointService {
    @Autowired
    private ParkPointDao parkPointDao;
    @Autowired
    private AccountConfigService accountConfigService;
    @Autowired
    private GeoreGionService georeGionService;
    @Autowired
    private AccountConfigExtService accountConfigExtService;


    @Override
    public void insert(ParkPoint parkPoint) {
        if (parkPoint.getCapacity() == null) {
            parkPoint.setCapacity(0);
        }
        parkPointDao.insert(parkPoint);
    }

    @Override
    public void insertBatch(List<ParkPoint> parkPoints) {
        parkPointDao.insertBatch(parkPoints);
    }

    @Override
    public void update(ParkPoint parkPoint) {
        parkPointDao.update(parkPoint);
    }

    @Override
    public void updateBatch(List<Map<String, Object>> maps) {
        List<List<Map<String, Object>>> parkPointGeo = ListUtils.partition(maps, 900);
        for (List<Map<String, Object>> parkPoint : parkPointGeo) {
            parkPointDao.updateBatch(parkPoint);
        }
    }

    @Override
    public void del(Integer parkPointId) {
        parkPointDao.del(parkPointId);
    }

    @Override
    public ParkPoint getByParkPointId(Integer parkPointId) {
        return parkPointDao.getByParkPointId(parkPointId);
    }

    @Override
    public List<ParkPoint> getByAccountId(Integer accountId) {
        return parkPointDao.getByAccountId(accountId);
    }

    @Override
    public List<ParkPoint> getByRegionId(List<Integer> regionId) {
        return parkPointDao.getByRegionId(StringUtil.getListString(regionId));
    }

    @Override
    public List<ParkPoint> getByRegionIdAndLevel(List<Integer> regionId, Integer level) {
        return parkPointDao.getByRegionIdAndLevel(StringUtil.getListString(regionId), level);
    }

    @Override
    public List<ParkPoint> getByAccountIdAndLevel(Integer accountId, Integer level) {
        return parkPointDao.getByAccountIdAndLevel(accountId, level);
    }

    @Override
    public List<ParkPoint> getByAccountIds(List<Integer> accountIds, String updateTime) {
        Map<String, Object> map = new HashMap<>();
        map.put(DaoParamConstant.accountIds, StringUtil.getListString(accountIds));
        map.put("updateTime", updateTime);
        return parkPointDao.getByAccountIds(map);
    }

    @Override
    public List<ParkPoint> getAccountIds(List<Integer> accountIds) {
        return parkPointDao.getAccountIds(StringUtil.getListString(accountIds));
    }


    @Override
    public ParkPoint getByMachineId(Integer machineId) {
        return parkPointDao.getByMachineId(machineId);
    }

    @Override
    public List<ParkPoint> getNear(Integer accountId, Point point, Integer mapType, List<Integer> regionId) {
        List<ParkPoint> result = new ArrayList<ParkPoint>();

        /**加载代理商所有站点*/
        List<ParkPoint> parkPoints = new LinkedList<>();
        if (CollUtil.isEmpty(regionId)) {
            parkPoints = getByAccountId(accountId);
        } else {
            parkPoints = getByRegionId(regionId);
        }
        if (parkPoints.size() > 0) {
            /**分组统计每个站点的投放数量*/
            List<ParkPointMachineCount> parkPoimtMachineCounts = new LinkedList<>();
            if (CollUtil.isEmpty(regionId)) {
                parkPoimtMachineCounts = groupByParkPoint(accountId);
            } else {
                parkPoimtMachineCounts = groupByRegionId(regionId);
            }
            Map<Integer, ParkPointMachineCount> parkMachineMap = new HashMap<Integer, ParkPointMachineCount>();
            for (ParkPointMachineCount ppmc : parkPoimtMachineCounts) {
                parkMachineMap.put(ppmc.getParkPointId(), ppmc);
            }

            for (ParkPoint pp : parkPoints) {
                /**当前站点投放数量*/
                ParkPointMachineCount parkPointMachineCount = parkMachineMap.get(pp.getParkPointId());
                if (parkPointMachineCount != null) {
                    pp.setMachineCount(parkPointMachineCount.getMachineCount());
                } else {
                    pp.setMachineCount(0);
                }

                Integer distance = (int) GeoUtil.getDistance(point, new Point(pp.getLo(), pp.getLa()));
                pp.setDistance(distance);
            }

            /**排序*/
            Collections.sort(parkPoints, ParkPoint::compareTo);

            /**选取最近的10个站点*/
            int size = 10;
            if (parkPoints.size() < 10) {
                size = parkPoints.size();
            }

            result = parkPoints.subList(0, size);
        }

        return result;
    }

    @Override
    public List<ParkPointMachineCount> groupByParkPoints(List<Integer> parkPoints) {
        List<ParkPointMachineCount> parkPointS = new LinkedList<>();
        List<List<Integer>> parkPointList = ListUtils.partition(parkPoints, 900);
        for (List<Integer> parkPoint : parkPointList) {
            parkPointS.addAll(parkPointDao.groupByParkPoints(StringUtil.getListString(parkPoint)));
        }

        return parkPointS;
    }

    @Override
    public List<ParkPointMachineCount> groupByParkPoint(Integer accountId) {
        return parkPointDao.groupByParkPoint(accountId);
    }

    @Override
    public List<ParkPointMachineCount> groupByRegionId(List<Integer> regionId) {
        return parkPointDao.groupByRegionId(StringUtil.getListString(regionId));
    }

    @Override
    public List<ParkPoint> getByAccountAndArea(Integer accountId, Double lo, Double la, Integer radius) {
        Double area = 0.00001D * radius;

        Map<String, Object> params = new HashMap<String, Object>();
        params.put(DaoParamConstant.accountId, accountId);
        params.put("minLo", lo - area);
        params.put("maxLo", lo + area);
        params.put("minLa", la - area);
        params.put("maxLa", la + area);

        return parkPointDao.getByAccountAndArea(params);
    }

    @Override
    public List<ParkPoint> getByRegionIdAndArea(List<Integer> regionId, Double lo, Double la, Integer radius) {
        Double area = 0.00001D * radius;
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("regionId", StringUtil.getListString(regionId));
        params.put("minLo", lo - area);
        params.put("maxLo", lo + area);
        params.put("minLa", la - area);
        params.put("maxLa", la + area);

        return parkPointDao.getByRegionIdAndArea(params);
    }

    /*public List<ParkPoint> getByAccountAndArea(Integer accountId,Double lo,Double la, Integer radius) {
        return parkPointH2Service.getByAccountAndArea(accountId,null, lo,la, radius);
    }
    public List<com.tbit.main.h2.pojo.ParkPoint> getByRegionIdAndArea(List<Integer> regionId,Double lo,Double la, Integer radius) {
        return parkPointH2Service.getByAccountAndArea(null,regionId, lo,la, radius);
    }*/
    @Override
    public ParkPoint checkInParkPoint(Integer accountId, Point point, Integer radius, Integer allowRange) {
        ParkPoint parkPointResult = null;

        List<ParkPoint> parkPoints = new ArrayList<ParkPoint>();

        /**判断是否是大站点模式*/
        AccountConfig accountConfig = accountConfigService.getByAccountId(accountId);
        if (accountConfig != null && accountConfig.getBigParkPoint() == 1) {
            parkPoints.addAll(getByAccountId(accountId));
        } else {
            parkPoints.addAll(getByAccountAndArea(accountId, point.getX(), point.getY(), radius));
        }


        /** 判断车辆是否在站点要求的经纬度内 */
        for (ParkPoint parkPoint : parkPoints) {
            if (parkPoint.getAllowRange() == -1) {
                AccountConfigExt allowRangeConfig = accountConfigExtService.getParamKey(parkPoint.getAccountId(), AccountConfigExtConstant.ALL_PARK_POINT_ALLOW_RANGE);
                if (allowRangeConfig != null){
                    parkPoint.setAllowRange(Integer.valueOf(allowRangeConfig.getParamValue()));
                }else {
                    parkPoint.setAllowRange(20);
                }
            }
            Double distince = null;
            /**默认圆形*/
            if (parkPoint.getParkPointType() == null) {
                parkPoint.setParkPointType(0);
            }

            /**是否指令判断误差范围*/
            if (allowRange != null) {
                if (parkPoint.getAllowRange() < allowRange) {
                    parkPoint.setAllowRange(allowRange);
                }
            } else {
                /**默认误差最小20*/
                if (parkPoint.getAllowRange() < 20) {
                    parkPoint.setAllowRange(20);
                }
            }

            if (parkPoint.getParkPointType().equals(1)) {
                /** 停车点是多边形 */
                List<Point> points = GeoUtil.getPoints(parkPoint.getPoints());

                /**判断是否在多边形内*/
                if (GeoUtil.IsPtInPoly(point, points)) {
                    parkPointResult = parkPoint;
                    break;
                }


                /**距离小于设定范围*/
                distince = GeoUtil.getDistance(point, points);
                if (distince < parkPoint.getAllowRange()) {
                    parkPoint.setDistanceTest(distince);
                    parkPointResult = parkPoint;
                    break;
                }
            } else {
                distince = MapUtils.GetDistance(point.getX(), point.getY(), parkPoint.getLo(), parkPoint.getLa());
                if (distince < parkPoint.getAllowRange()) {
                    parkPointResult = parkPoint;
                    break;
                }
            }

        }

        return parkPointResult;
    }

    @Override
    public ParkPoint checkInParkPointNew(Integer accountId, Point point, Integer radius, Integer allowRange) {
        ParkPoint parkPointResult = null;

        List<ParkPoint> parkPoints = new ArrayList<ParkPoint>();

        /**判断是否是大站点模式*/
        AccountConfig accountConfig = accountConfigService.getByAccountId(accountId);

        if (accountConfig != null && accountConfig.getBigParkPoint() == 1) {
            parkPoints.addAll(getByAccountId(accountId));
        } else {
            parkPoints.addAll(getByAccountAndArea(accountId, point.getX(), point.getY(), radius));
        }

        Map<Double, ParkPoint> mapParkPoint = new HashMap<>();

        /** 判断车辆是否在站点要求的经纬度内 */
        for (ParkPoint parkPoint : parkPoints) {
            if (parkPoint.getAllowRange() == -1) {
                AccountConfigExt allowRangeConfig = accountConfigExtService.getParamKey(parkPoint.getAccountId(), AccountConfigExtConstant.ALL_PARK_POINT_ALLOW_RANGE);
                if (allowRangeConfig != null){
                    parkPoint.setAllowRange(Integer.valueOf(allowRangeConfig.getParamValue()));
                }else {
                    parkPoint.setAllowRange(20);
                }
            }
            Double distince = null;
            /**默认圆形*/
            if (parkPoint.getParkPointType() == null) {
                parkPoint.setParkPointType(0);
            }

            /**是否指令判断误差范围*/
            if (allowRange != null) {
                if (parkPoint.getAllowRange() < allowRange) {
                    parkPoint.setAllowRange(allowRange);
                }
            } else {
                /**默认误差最小20*/
                if (parkPoint.getAllowRange() < 20) {
                    parkPoint.setAllowRange(20);
                }
            }

            if (parkPoint.getParkPointType().equals(1)) {
                /** 停车点是多边形 */
                List<Point> points = GeoUtil.getPoints(parkPoint.getPoints());

                /**判断是否在多边形内*/
                if (GeoUtil.IsPtInPoly(point, points)) {
                    parkPointResult = parkPoint;
                    parkPointResult.setInside(1);
                    parkPointResult.setDistance(0);
                    mapParkPoint.put(0.0, parkPointResult);
                }


                /**距离小于设定范围*/
                distince = GeoUtil.getDistance(point, points);
                if (distince <= parkPoint.getAllowRange()) {
                    parkPointResult = parkPoint;
                    parkPointResult.setInside(1);
                    parkPointResult.setDistance(distince.intValue());
                    mapParkPoint.put(distince, parkPointResult);
                } else {
                    if (mapParkPoint.isEmpty()) {
                        parkPointResult = parkPoint;
                        parkPointResult.setInside(0);
                        parkPointResult.setDistance(distince.intValue());
                        mapParkPoint.put(distince, parkPointResult);
                    }
                }
            } else {
                distince = MapUtils.GetDistance(point.getX(), point.getY(), parkPoint.getLo(), parkPoint.getLa());
                if (distince <= parkPoint.getAllowRange()) {
                    parkPointResult = parkPoint;
                    parkPointResult.setInside(1);
                    parkPointResult.setDistance(distince.intValue());
                    mapParkPoint.put(distince, parkPointResult);
                } else {
                    if (mapParkPoint.isEmpty()) {
                        parkPointResult = parkPoint;
                        parkPointResult.setInside(0);
                        parkPointResult.setDistance(distince.intValue());
                        mapParkPoint.put(distince, parkPointResult);
                    }
                }
            }

        }

        if (mapParkPoint != null && !mapParkPoint.isEmpty()) {
            Set<Double> set = mapParkPoint.keySet();
            Object[] obj = set.toArray();
            Arrays.sort(obj);
            parkPointResult = mapParkPoint.get(obj[0]);
        }

        return parkPointResult;
    }

    @Override
    public List<HeatMapStat> statByPark(Integer accountId, Integer type, String startTime, String endTime) {
        Map<String, Object> map = new HashMap<>();
        map.put(DaoParamConstant.accountId, accountId);
        map.put(DaoParamConstant.startTime, startTime);
        map.put(DaoParamConstant.endTime, endTime);
        if (type == 0) {
            return parkPointDao.statByStartPark(map);
        } else {
            return parkPointDao.statByEndPark(map);
        }
    }

    @Override
    public List<Integer> getNotRegionId() {
        return parkPointDao.getNotRegionId();
    }

    @Override
    public void delRegionId(List<Integer> parkPointIds) {
        List<List<Integer>> parkPointGeo = ListUtils.partition(parkPointIds, 900);
        for (List<Integer> parkPoint : parkPointGeo) {
            parkPointDao.delRegionId(StringUtil.getListString(parkPoint));
        }
    }

    @Override
    public List<ParkPoint> getByParkPoints(List<Integer> parkPoints) {
        if (CollUtil.isEmpty(parkPoints)) {
            return Collections.emptyList();
        }

        List<ParkPoint> parkPointS = new LinkedList<>();
        List<List<Integer>> parkPointList = ListUtils.partition(parkPoints, 900);
        for (List<Integer> parkPoint : parkPointList) {
            parkPointS.addAll(parkPointDao.getByParkPoints(StringUtil.getListString(parkPoint)));
        }

        return parkPointS;
    }

    @Override
    public List<ParkPoint> getByRegionIds(Integer accountId, Integer accountUserId, List<Integer> regionId) {
        Map<String, Object> params = new HashMap<>();
        if (CollUtil.isEmpty(regionId)) {
            params.put("exist", "'exist'");
            regionId = georeGionService.getByAccountUserId(accountUserId);
        } else {
            params.put("exist", null);
        }
        params.put("regionId", StringUtil.getListString(regionId));
        params.put(DaoParamConstant.accountId, accountId);

        return parkPointDao.getByRegionIds(params);
    }

    @Override
    public List<ParkPoint> getAll() {
        return parkPointDao.getAll();
    }

    @Override
    public void test() {
        Map<String, Object> map = new HashMap<>();
        map.put(DaoParamConstant.accountId, 10877);
        map.put(DaoParamConstant.startTime, "2021-03-01");
        map.put(DaoParamConstant.endTime, "2022-03-01 23:59:59");
        parkPointDao.test(map);
    }

    @Override
    public List<ParkPoint> getAccountIdName(Integer accountId, String name) {
        Map<String, Object> map = new HashMap<>();
        map.put(DaoParamConstant.accountId, accountId);
        map.put("name", name);
        return parkPointDao.getAccountIdName(map);
    }

}